﻿using System;
using System.Collections.Generic;

namespace RNNLibrary
{
    /// <summary>
    /// 神经元
    /// </summary>
    public class Neurons
    {
        #region 属性
        /// <summary>
        /// X值
        /// </summary>
        public decimal X { private get; set; }
        /// <summary>
        /// 上一次时序
        /// </summary>
        private Neurons Previous { get; set; }
        /// <summary>
        /// 当前时序计数
        /// </summary>
        public int CurrentT { get; private set; }
        /// <summary>
        /// 当前时序值
        /// </summary>
        private decimal CurrentTH { get; set; }
        /// <summary>
        /// 输出层实际值
        /// </summary>
        public decimal Aims { get;private set; }
        /// <summary>
        /// 输出层的导数
        /// </summary>
        private decimal DeltaAims { get; set; }
        /// <summary>
        /// 输出权值的待更新值
        /// </summary>
        private decimal DeltaV { get; set; }
        /// <summary>
        /// 上一隐层权值的待更新值
        /// </summary>
        private decimal DeltaW { get; set; }
        /// <summary>
        /// 输入权值的待更新值
        /// </summary>
        private decimal DeltaU { get; set; }
        /// <summary>
        /// 隐层输出的导数
        /// </summary>
        public decimal DeltaH { get;private set; }
        /// <summary>
        /// 输出层目标值
        /// </summary>
        private decimal Y { get; set; }
        /// <summary>
        /// 损失值
        /// </summary>
        public decimal LossVal { get;private set; }
        #endregion

        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        public Neurons(decimal x,decimal y)
        {
            X = x;
            Y = y;
            CurrentT = 1;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="previous"></param>
        public Neurons(decimal x, decimal y, Neurons previous)
            : this(x, y)
        {
            Previous = previous;
            CurrentT = previous.CurrentT + 1;
        }
        #endregion

        #region 公共方法
        /// <summary>
        /// 一次前向
        /// </summary>
        /// <param name="W">上一时序隐藏层的权值</param>
        /// <param name="U">输入量的权值</param>
        /// <param name="V">输出层的权值</param>
        public void Forward(decimal W, decimal U, decimal V)
        {
            CurrentTH = 0;
            CurrentTH += X * U;
            if (Previous != null)
            {
                CurrentTH += Previous.CurrentTH * W;
            }
            CurrentTH = (decimal)Sigmoid((double)CurrentTH);
            Aims = ReLu(CurrentTH * V);
        }
        /// <summary>
        /// 一次反向
        /// </summary>
        /// <param name="nextDeltaH">下一时序的隐层导数</param>
        /// <param name="W">上一时序隐藏层的权值</param>
        /// <param name="V">输出层的权值</param>
        public void Reverse(decimal nextDeltaH, decimal W, decimal V)
        {
            Loss();
            DerivativeAims();
            DerivativeH(nextDeltaH, W, V);
            DeltaV = CurrentTH * DeltaAims;
            DeltaU = X * DeltaH;
            if (Previous != null && CurrentT != 1)
            {
                DeltaW = Previous.CurrentTH * DeltaH;
            }
            else
            {
                DeltaW = 0;
            }
        }
        /// <summary>
        /// 累加待更新参数的导数值
        /// </summary>
        /// <param name="w"></param>
        /// <param name="u"></param>
        /// <param name="v"></param>
        public void SumDeltaHyperparameter(ref decimal w,ref decimal u,ref decimal v)
        {
            w += DeltaW;
            u += DeltaU;
            v += DeltaV;
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 代价函数
        /// </summary>
        private void Loss()
        {
            LossVal = Y - Aims;
        }
        /// <summary>
        /// Y’的导数
        /// </summary>
        /// <returns></returns>
        private void DerivativeAims()
        {
            DeltaAims = LossVal * DerivativeReLu(Aims);
        }
        /// <summary>
        /// H的导数
        /// </summary>
        /// <param name="nextDeltaH">下一时序隐藏层H的导数</param>
        /// <param name="W">上一时序隐藏层的权值</param>
        /// <param name="V">输出层的权值</param>
        /// <returns></returns>
        private void DerivativeH(decimal nextDeltaH, decimal W, decimal V)
        {
            DeltaH= nextDeltaH * W + DeltaAims * V * (decimal)DerivativeSigmoid((double)CurrentTH);
        }
        /// <summary>
        /// Sigmoid激活
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        private double Sigmoid(double val)
        {
            return 1 / (1 + Math.Exp(-val));
        }
        /// <summary>
        /// Sigmoid导数
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        private double DerivativeSigmoid(double val)
        {
            return val * (1 - val);
        }
        /// <summary>
        /// ReLu激活
        /// </summary>
        private decimal ReLu(decimal val)
        {
            if (val <= 0)
            {
                return 0;
            }
            else
            {
                return val;
            }
        }
        /// <summary>
        /// ReLu导数
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        private decimal DerivativeReLu(decimal val)
        {
            if(val>0)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        /// <summary>
        /// TanH函数
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private double TanH(double x)
        {
            return Math.Sinh(x) / Math.Cosh(x);
        }
        /// <summary>
        /// TanH导数
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private double DerivativeTanH(double x)
        {
            return 1 - Square(TanH(x));
        }
        /// <summary>
        /// 平方
        /// </summary>
        /// <returns></returns>
        private double Square(double val)
        {
            return val * val;
        }
        #endregion
    }
}
